package cn.swust.HuXinMeng.tilegame;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.util.Iterator;

import cn.swust.HuXinMeng.graphics.ScreenManager;
import cn.swust.HuXinMeng.graphics.Sprite;
import cn.swust.HuXinMeng.tilegame.sprites.Creature;

public class TileMapRenderer {

	private static final int TILE_SIZE = 64;
	private static final int TILE_SIZE_BITS = (int)(Math.log(TILE_SIZE)/Math.log(2));
	private Image background;
	public static int pixelsToTiles(float pixels) {
		return pixelsToTiles(Math.round(pixels));
	}
	public static int pixelsToTiles(int pixels) {
		return pixels >> TILE_SIZE_BITS;
	}

	public static int tilesToPixels(int numTiles) {
		return numTiles << TILE_SIZE_BITS;
	}

	public void withBackground(Image background) {
		this.background = background;
	}

	public void draw(Graphics2D g, TileMap map, ScreenManager screen) {
		Sprite player = map.getPlayer();
		int mapWidth = tilesToPixels(map.getWidth());
		int mapHeight = tilesToPixels(map.getHeight());

		int screenWidth = screen.getWidth();
		int screenHeight = screen.getHeight();
		
		int offsetX = screenWidth / 2 - Math.round(player.getX()) - TILE_SIZE;
		offsetX = Math.min(offsetX, 0);
		offsetX = Math.max(offsetX, screenWidth - mapWidth);

		int offsetY = screenHeight / 2 - Math.round(player.getY()) - player.getHeight();
		offsetY = Math.min(offsetY, 0);
		offsetY = Math.max(offsetY, screenHeight - mapHeight);

		drawBackground(g, mapWidth, screenWidth, screenHeight, offsetX);
		drawVisibleTiles(g, map, screenWidth, screenHeight, offsetX, offsetY);
		drawPlayer(g, player, offsetX, offsetY);
		drawOtherSprites(g, map, screenWidth, screenHeight, offsetX, offsetY);
	}
	
	private void drawBackground(Graphics2D g, int mapWidth, int screenWidth, int screenHeight, int offsetX) {
		if (background == null || screenHeight > background.getHeight(null)) {
			g.setColor(Color.black);
			g.fillRect(0, 0, screenWidth, screenHeight);
		}
		if (background != null) {
			int backgroundX = offsetX * (screenWidth - background.getWidth(null)) / (screenWidth - mapWidth);
			int backgroundY = screenHeight - background.getHeight(null);

			g.drawImage(background, backgroundX, backgroundY, null);
		}
	}
	
	private void drawVisibleTiles(Graphics2D g, TileMap map, int screenWidth, int screenHeight, 
			int offsetX, int offsetY) {

		int firstTileX = pixelsToTiles(-offsetX);
		int lastTileX = firstTileX + pixelsToTiles(screenWidth) + 1;
		for (int y = 0; y < screenHeight; y++) {
			for (int x = firstTileX; x <= lastTileX; x++) {
				Image image = map.getTile(x, y);
				if (image != null) {
					g.drawImage(image, tilesToPixels(x) + offsetX, tilesToPixels(y) + offsetY, null);
				}
			}
		}
	}
	
	private void drawPlayer(Graphics2D g, Sprite player, int offsetX, int offsetY) {
		// draw player
		g.drawImage(player.getImage(), Math.round(player.getX()) + offsetX, Math.round(player.getY()) + offsetY, null);
	}
	
	private void drawOtherSprites(Graphics2D g, TileMap map, int screenWidth, int screenHeight, 
			int offsetX, int offsetY) {
		
		// draw sprites
		int firstTileX = pixelsToTiles(-offsetX);
		int lastTileX = firstTileX + pixelsToTiles(screenWidth) + 1;
		int firstTileY = pixelsToTiles(-offsetY);
		int lastTileY = firstTileY + pixelsToTiles(screenHeight) + 1;
		Iterator<Sprite> i = map.getSprites();
		while (i.hasNext()) {
			Sprite sprite = i.next();
			int spriteX = Math.round(sprite.getX());
			int spriteY = Math.round(sprite.getY());

			if (firstTileX > pixelsToTiles(spriteX + sprite.getWidth()) || lastTileX <= pixelsToTiles(spriteX)
					|| firstTileY > pixelsToTiles(spriteY + sprite.getHeight()) || lastTileY <= pixelsToTiles(spriteY)) {
				continue;
			}
			
			g.drawImage(sprite.getImage(), spriteX + offsetX, spriteY + offsetY, null);
			if (sprite instanceof Creature) {
				((Creature) sprite).wakeUp();
			}
		}
	}

}
